#include <stdio.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "uart.h"
//#include "uart_drv.h"
#include "app_types.h"
#include "hardware.h"
#include "error.h"

#define RBUF_SIZE		32

struct _UART_RX
{
	unsigned char rbuf[RBUF_SIZE];
	unsigned char r_in;
	unsigned char r_out;
};

typedef struct _UART_RX UART_RX;

volatile UART_RX rx;

ISR(USART0_RX_vect)
{
	uint8_t new_idx;

	if ((UCSR0A & (1<<FE0)) == 0) // nur wenn kein FrameError vorliegt
	{
		new_idx = rx.r_in + 1;
		if (new_idx >= RBUF_SIZE)
			new_idx = 0;

		rx.rbuf[rx.r_in] = UDR0;
		rx.r_in = new_idx;
	}
}

void uart_init()
{
	rx.r_in = 0;
	rx.r_out = 0;

//	DDRD &= ~(1<<PD0); // RXD als Eingang
	DDRD |= (1<<PD1); // TXD als Ausgang

	//Enable TXEN im Register UCR TX-Data Enable & RX Enable
	UCSR0B = (1 << TXEN0) | (1 << RXEN0);
    // UART Double Speed (U2X)
	UCSR0A |= (1 << U2X0);
	// RX-Interrupt Freigabe
	UCSR0B |= (1 << RXCIE0);
	// TX-Interrupt Freigabe
	//UCSR0B |= (1 << TXCIE0);
	// USART0 Baud Rate Register
	// set clock divider
	UBRR0  = 64; // 38.4Baud @ 20MHz
	//UBRR0  = 259; // 9.6Baud @ 20MHz
}

void uart_exit()
{
	UCSR0B &= ~((1<<RXEN0) | (1<<TXEN0));
}

void uart_putc(unsigned char c)
{
    while(!(((UCSR0A &   1<<UDRE0)) >> UDRE0 ))
		check_Hang(PLACE_UART_WAIT_BYTE_SEND);

    UDR0 = c;
}

void uart_putns(char *string, uint8_t len)
{
	while(*string)
	{
		if (len == 0)
			break;
		uart_putc(*string);
		string++;
		len--;	
	}	
}

void uart_puts(char *string)
{
	while(*string){
		uart_putc(*string);
		string++;	
	}	
}

void uart_puts_P(const char *string)
{
	while(pgm_read_byte(string))
	{
		uart_putc(pgm_read_byte(string));
		string++;	
	}	
}

uint8_t uart_data_available()
{
	if (rx.r_in != rx.r_out)
		return 1; // new data available
	else
		return 0; // no data available
}

// return receive value between 0..255 and an negative value on error 
int uart_getc()
{
	unsigned char new_idx;
	unsigned char c;

	if (uart_data_available() == 0)
		return -1;

	new_idx = rx.r_out + 1;
	if (new_idx >= RBUF_SIZE)
		new_idx = 0;
	c = rx.rbuf[rx.r_out];
	rx.r_out = new_idx;
	return (int)c;
}
